package cn.com.cpic.insurance.rulecheck.core;

import groovy.lang.GroovyClassLoader;

import java.util.List;

import org.apache.struts2.ServletActionContext;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.tools.Compiler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.com.cpic.insurance.rulecheck.consumer.BooleanResult;
import cn.com.cpic.insurance.rulecheck.consumer.BooleanRuleConsumer;
import cn.com.cpic.insurance.rulecheck.core.operator.Operator;
import cn.com.cpic.insurance.rulecheck.core.operator.OperatorUtils;
import cn.com.cpic.insurance.rulecheck.entity.Condition;
import cn.com.cpic.insurance.rulecheck.entity.ConditionItem;
import cn.com.cpic.insurance.rulecheck.entity.Rule;
import cn.com.cpic.insurance.rulecheck.entity.RuleConsumer;
import cn.com.cpic.insurance.rulecheck.entity.RuleItem;
import cn.com.cpic.insurance.rulecheck.service.ConditionItemService;
import cn.com.cpic.insurance.rulecheck.service.ConditionService;
import cn.com.cpic.insurance.rulecheck.service.RuleConsumerService;
import cn.com.cpic.insurance.rulecheck.service.RuleItemService;
import cn.com.cpic.insurance.rulecheck.service.RuleService;

import com.google.common.collect.Lists;

@Service
public class ConditionHelper {

	private static String path;
	private static Compiler compiler;
	private static GroovyClassLoader classLoader;

	private Context context;
	private List<Rule> rules = Lists.newArrayList();

	private ConditionService conditionService;
	private ConditionItemService conditionItemService;
	private RuleService ruleService;
	private RuleItemService ruleItemService;
	private RuleConsumerService ruleConsumerService;

	static {
		path = ServletActionContext.getServletContext().getRealPath("WEB-INF/groovyClasses");
		CompilerConfiguration cc = new CompilerConfiguration();
		cc.setTargetDirectory(path);
		compiler = new Compiler(cc);
		classLoader = new GroovyClassLoader(ConditionHelper.class.getClassLoader());
		classLoader.addClasspath(path);
	}

	public void init(Context context) {
		this.context = context;
		prepareRules();
	}

	public void clean() {
		this.rules = Lists.newArrayList();
	}

	public Object getRule(String name) {
		if (context.getRules().containsKey(name)) {
			return context.getRules().get(name);
		}
		return "未定义的规则";
	}

	public BooleanResult consume() throws Exception {
		BooleanResult result = new BooleanResult();
		for (RuleConsumer consumer : getRuleConsumers()) {
			if (consumer.getLanguage() == 1) {
				compiler.compile(consumer.getName(), consumer.getCode());
				Class<?> clazz = classLoader.loadClass(consumer.getName());
				Object newInstance = clazz.newInstance();
				if (newInstance instanceof BooleanRuleConsumer) {
					BooleanRuleConsumer instance = (BooleanRuleConsumer) newInstance;
					result = BooleanResult.merge(result, instance.consume(context, null));
				}
			}
		}
		return result;
	}

	private List<RuleConsumer> getRuleConsumers() {
		List<RuleConsumer> consumers = Lists.newArrayList();
		for (Rule rule : rules) {
			consumers.addAll(ruleConsumerService.getRuleConsumerList(rule.getRuleDefinitionId()));
		}
		return consumers;
	}

	/**
	 * 取得所有条件，依次匹配，将所有匹配的条件的规则合并到一个集合中。
	 * @return
	 */
	private void prepareRules() {
		for (Condition condition : conditionService.getConditionList()) {
			if (calculateCondition(condition)) {
				rules.addAll(ruleService.getRuleList(condition.getId()));
			}
		}
		for (Rule rule : rules) {
			for (RuleItem item : ruleItemService.getRuleItemList(rule.getId())) {
				context.getRules().put(item.getParamName(), item.getParamValue());
			}
		}
	}

	/**
	 * 计算一个指定的条件是否匹配。
	 * @param condition
	 * @return
	 */
	private boolean calculateCondition(Condition condition) {
		for (ConditionItem item : conditionItemService.getConditionItemList(condition.getId())) {
			Operator operator = OperatorUtils.getOperator(item.getParamOperator());
			Object leftValue = context.getRtParam().get(item.getParamName());
			String rightValue = item.getParamValue();
			if (!operator.calculate(leftValue.toString().trim(), rightValue.toString().trim())) {
				return false;
			}
		}
		return true;
	}

	@Autowired
	public void setConditionService(ConditionService conditionService) {
		this.conditionService = conditionService;
	}

	@Autowired
	public void setConditionItemService(ConditionItemService conditionItemService) {
		this.conditionItemService = conditionItemService;
	}

	@Autowired
	public void setRuleService(RuleService ruleService) {
		this.ruleService = ruleService;
	}

	@Autowired
	public void setRuleItemService(RuleItemService ruleItemService) {
		this.ruleItemService = ruleItemService;
	}

	@Autowired
	public void setRuleConsumerService(RuleConsumerService ruleConsumerService) {
		this.ruleConsumerService = ruleConsumerService;
	}
}
